package com.bestcem.xm.txtanls.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.ObjectId;
import cn.hutool.core.util.StrUtil;
import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.txtanls.dao.*;
import com.bestcem.xm.txtanls.dao.dto.PageDTO;
import com.bestcem.xm.txtanls.dao.mapper.TrainModelMapper;
import com.bestcem.xm.txtanls.entity.pojo.ProjectDO;
import com.bestcem.xm.txtanls.entity.pojo.ProjectFieldDO;
import com.bestcem.xm.txtanls.enums.AnalysisStatusEnum;
import com.bestcem.xm.txtanls.enums.IndustryCodeEnum;
import com.bestcem.xm.txtanls.grpc.service.param.PageParam;
import com.bestcem.xm.txtanls.grpc.service.param.TextFieldParam;
import com.bestcem.xm.txtanls.grpc.service.param.analysis.CreateAnalysisProjectParam;
import com.bestcem.xm.txtanls.grpc.service.param.analysis.LinkageUpdateParam;
import com.bestcem.xm.txtanls.grpc.service.param.analysis.ListAnalysisProjectParam;
import com.bestcem.xm.txtanls.grpc.service.param.analysis.UpdateAnalysisProjectParam;
import com.bestcem.xm.txtanls.service.AnalysisProjectService;
import com.bestcem.xm.txtanls.service.TrainModelService;
import com.bestcem.xm.txtanls.service.dto.PageResultDTO;
import com.bestcem.xm.txtanls.service.dto.analysis.AnalysisProjectDTO;
import com.bestcem.xm.txtanls.util.AnalysisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * 文本分析项目相关业务接口
 *
 * @author XiaoYunSong <ys.xiao@idiaoyan.com>
 * @version v1.0
 * @date 2021/5/8
 */
@Slf4j
@Service
public class AnalysisProjectServiceImpl implements AnalysisProjectService {

    @Resource
    private AnalysisUtil analysisUtil;

    @Resource
    private TopicDao topicDao;
    @Resource
    private AnalysisProjectDao analysisProjectDao;
    @Resource
    private ViewpointDao viewpointDao;
    @Resource
    private RecordDao recordDao;
    @Resource
    private HiddenKeywordDao hiddenKeywordDao;
    @Resource
    private JoinInfoDao joinInfoDao;
    @Resource
    private SurveyTxtanlsFieldDao surveyTxtanlsFieldDao;

    @Resource
    private TrainModelMapper trainModelMapper;

    @Resource
    private TrainModelService trainModelService;

    @Override
    public ServiceResult<AnalysisProjectDTO> getById(String projectId) {
        ProjectDO entity = analysisProjectDao.findById(projectId);
        if (Objects.isNull(entity)) {
            return ServiceResult.fail("文本分析项目不存在");
        }
        AnalysisProjectDTO dto = AnalysisProjectDTO.analysisEntityToDTO(entity);
        dto = analysisUtil.setUserInfo(dto);
        dto = analysisUtil.setProjectTrainInfo(dto);

        return ServiceResult.success(dto);
    }

    @Override
    public ServiceResult<PageResultDTO<AnalysisProjectDTO>> listProject(ListAnalysisProjectParam param) {
        PageParam pageParam = param.getPageParam();
        Boolean admin = param.getAdmin();
        String name = param.getName();
        Integer status = param.getStatus();
        PageDTO<ProjectDO> pageDTO;
        List<AnalysisProjectDTO> dtoList = new ArrayList<>();
        // 如果是超级管理员的话, 查询公司下所有项目, 否则查询对应用户创建的项目
        if (admin) {
            pageDTO = analysisProjectDao.findOrganizationAllProject(param.getOrgId(), pageParam, name, status);
        } else {
            pageDTO = analysisProjectDao.findUserAllProject(param.getUserId(), pageParam, name, status);
        }
        List<ProjectDO> entityList = pageDTO.getData();
        if (Objects.isNull(entityList) || entityList.isEmpty()) {
            return ServiceResult.success(new PageResultDTO<>(Collections.emptyList(), pageDTO.getTotal()));
        }
        // 获取第一个项目的对应用户信息, 如果不是超级管理员就全都用这个用户名
        String username = analysisUtil.getUserName(entityList.get(0).getCreator());
        for (ProjectDO projectDO : entityList) {
            AnalysisProjectDTO dto = AnalysisProjectDTO.analysisEntityToDTO(projectDO);
            // 兼容旧示例项目的前端展示
            if (("文本分析项目示例".equals(dto.getName()) || "示例文本分析项目".equals(dto.getName()))
                    && ("餐饮数据源demo".equals(dto.getTableName()) || "示例文本分析项目数据源".equals(dto.getTableName()))) {
                dto.setSample(true);
            }

            dto.setCreator(username);
            if (admin) {
                dto = analysisUtil.setUserInfo(dto);
            }
            dtoList.add(dto);
        }
        return ServiceResult.success(new PageResultDTO<>(dtoList, pageDTO.getTotal()));
    }

    @Override
    public ServiceResult<AnalysisProjectDTO> createProject(CreateAnalysisProjectParam param) {
        if (log.isInfoEnabled()) {
            log.info("[txtanls] 文本分析创建项目 start");
        }
        ProjectDO oldEntity = analysisProjectDao.findByNameAndCreator(param.getName(), param.getUserId());
        if (Objects.nonNull(oldEntity)) {
            return ServiceResult.fail("项目名称已存在");
        }
        ProjectDO entity = new ProjectDO();
        entity.setOrgId(param.getOrgId());
        entity.setCreator(param.getUserId());
        entity.setName(param.getName());
        entity.setTableId(param.getTableId());
        entity.setTableName(param.getTableName());
        entity.setTimeFieldId(param.getTimeFieldId());
        entity.setTimeFieldName(param.getTimeFieldName());
        entity.setStatus(AnalysisStatusEnum.ACTIVE.getIndex());
        entity.setCreatedId(param.getUserId());
        entity.setUpdatedId(param.getUserId());
        entity.setModelId(param.getModelId());
        List<ProjectFieldDO> textFields = new ArrayList<>();
        for (TextFieldParam textField : param.getTextFields()) {
            textFields.add(new ProjectFieldDO(textField.getId(), textField.getValue(), null));
        }
        entity.setTextFieldList(textFields);
        // 插入项目信息
        entity = analysisProjectDao.insertProject(entity);
        if (Objects.isNull(entity.getId())) {
            return ServiceResult.fail("文本分析项目创建失败");
        }

        try {
            // 初始化项目联动信息
            if (!analysisUtil.initAnalysisProject(entity.getId(), entity.getOrgId(), param.getModelId())) {
                analysisUtil.rollbackProject(entity.getId());
                return ServiceResult.fail("文本分析项目创建失败");
            }
        } catch (Exception e) {
            log.error("[Txtanls] 初始化项目联动信息异常; ErrorMsg:{}", e.getMessage(), e);
            analysisUtil.rollbackProject(entity.getId());
            return ServiceResult.fail("文本分析项目创建失败");
        }

        if (log.isInfoEnabled()) {
            log.info("[txtanls] 初始化项目联动信息完成 projectId={}， orgId={}", entity.getId(), entity.getOrgId());
        }
        // 查找最新的项目信息
        ProjectDO projectDO = analysisProjectDao.findById(entity.getId());
        // 设置项目对应的用户信息
        AnalysisProjectDTO dto = AnalysisProjectDTO.analysisEntityToDTO(projectDO);
        dto = analysisUtil.setUserInfo(dto);
        // 设置项目的兜底训练状态信息,防止对应字段为空
        dto = analysisUtil.setProjectTrainInfo(dto);
        return ServiceResult.success(dto);
    }

    @Override
    public ServiceResult<AnalysisProjectDTO> updateProject(UpdateAnalysisProjectParam param) {
        String projectId = param.getProjectId();
        ProjectDO oldEntity = analysisProjectDao.findById(projectId);
        if (Objects.isNull(oldEntity)) {
            return ServiceResult.fail("项目不存在");
        }
        ProjectDO otherEntity = analysisProjectDao.findByNameAndCreator(param.getName(), param.getUserId());
        if (Objects.nonNull(otherEntity) && !otherEntity.getId().equals(projectId)) {
            return ServiceResult.fail("项目名称已存在");
        }
        ProjectDO projectDO = new ProjectDO();
        projectDO.setUserId(param.getUserId());
        projectDO.setOrgId(param.getOrgId());
        projectDO.setId(param.getProjectId());
        projectDO.setName(param.getName());
        projectDO.setTableId(param.getTableId());
        projectDO.setTableName(param.getTableName());
        projectDO.setTimeFieldId(param.getTimeFieldId());
        projectDO.setTimeFieldName(param.getTimeFieldName());
        List<TextFieldParam> textFields = param.getTextFields();
        List<ProjectFieldDO> textFieldList = new ArrayList<>();
        for (TextFieldParam textFieldParam : textFields) {
            textFieldList.add(new ProjectFieldDO(textFieldParam.getId(), textFieldParam.getValue(), null));
        }
        projectDO.setTextFieldList(textFieldList);
        int result = analysisProjectDao.updateProject(projectDO);
        if (result == 0) {
            return ServiceResult.fail("项目更新失败");
        }

        ProjectDO entity = analysisProjectDao.findById(projectId);
        Boolean retrain = isRetrainProject(oldEntity, entity);
        if (retrain) {
            // 删除项目训练信息
            deleteProjectTrainInfo(projectId);
            // 开启重新训练
            analysisUtil.retrainProject(entity);
        }

        AnalysisProjectDTO dto = AnalysisProjectDTO.analysisEntityToDTO(entity);
        dto = analysisUtil.setUserInfo(dto);
        return ServiceResult.success(dto);
    }

    @Override
    public ServiceResult<String> deleteProject(String projectId) {
        int result = analysisProjectDao.deleteProject(projectId);
        if (result == 0) {
            return ServiceResult.fail("文本分析项目删除失败");
        }

        boolean topicDeleteResult = topicDao.deleteProjectAllTopic(projectId);
        if (!topicDeleteResult) {
            log.error("[Txtanls] 文本分析项目下属话题信息删除失败, projectId: {}", projectId);
        }
        boolean hiddenDeleteResult = hiddenKeywordDao.deleteProjectAllHidden(projectId);
        if (!hiddenDeleteResult) {
            log.error("[Txtanls] 文本分析项目下属隐藏关键词信息删除失败, projectId: {}", projectId);
        }
        boolean viewpointDeleteResult = viewpointDao.deleteProjectAllViewpoint(projectId);
        if (!viewpointDeleteResult) {
            log.error("[Txtanls] 文本分析项目下属观点信息删除失败, projectId: {}", projectId);
        }
        boolean fieldDeleteResult = surveyTxtanlsFieldDao.deleteProjectAllFiled(projectId);
        if (!fieldDeleteResult) {
            log.error("[Txtanls] 文本分析项目下属字段信息删除失败, projectId: {}", projectId);
        }
        boolean recordDeleteResult = recordDao.deleteProjectAllRecord(projectId);
        if (!recordDeleteResult) {
            log.error("[Txtanls] 文本分析项目下属原话信息删除失败, projectId: {}", projectId);
        }
        boolean joinInfoDeleteResult = joinInfoDao.deleteProjectAllJoinInfo(projectId);
        if (!joinInfoDeleteResult) {
            log.error("[Txtanls] 文本分析项目下属 join info 信息删除失败, projectId: {}", projectId);
        }
        return ServiceResult.success(projectId);
    }

    @Override
    public void linkageUpdate(LinkageUpdateParam param) {
        if (log.isInfoEnabled()) {
            log.info("[Txtanls] 文本分析联动更新 {}", param);
        }
        int result = 0;
        if (param.getWorktable()) {
            result = analysisProjectDao.updateTableNameByTableId(param.getId(), param.getName());
        } else {
            result = analysisUtil.updateFieldByColumn(param);
        }
        if (result == 0) {
            log.error("[Txtanls] 数据源更新联动更新文本分析项目失败; Param: {}", param);
            ServiceResult.fail("更新失败");
            return;
        }
        ServiceResult.success();
    }

    /**
     * 删除项目训练信息，包含原话记录以及joininfo
     *
     * @param projectId 项目id
     * @author jy.zhao
     * @date 2021/11/23 14:45
     **/
    @Override
    public void deleteProjectTrainInfo(String projectId) {
        boolean recordDeleteResult = recordDao.deleteProjectAllRecord(projectId);
        if (!recordDeleteResult) {
            log.error("[Txtanls] 文本分析项目下属原话信息删除失败, projectId: {}", projectId);
        }
        boolean joinInfoDeleteResult = joinInfoDao.deleteProjectAllJoinInfo(projectId);
        if (!joinInfoDeleteResult) {
            log.error("[Txtanls] 文本分析项目下属 join info 信息删除失败, projectId: {}", projectId);
        }
    }

    @Override
    public void setProjectModelId() {
        List<String> orgIds = analysisProjectDao.listGroupOrgId();
        String defaultModelId = trainModelService.getDefaultModelId(IndustryCodeEnum.DIET.getIndex());
        long count = 0L;
        for (String orgId : orgIds) {
            if (ObjectId.isValid(orgId)) {
                String trainModelId = trainModelService.getTrainModelId(orgId);
                Long updateCount = analysisProjectDao.updateSetModelId(orgId, trainModelId);
                count += updateCount;
                log.error("[txtanls]:文本分析项目单次初始化,组织id:{},训练id条数：{},", orgId, updateCount);
            }
        }
        log.error("[txtanls]:文本分析项目初始化训练id条数：{}", count);
        Long updateCount = analysisProjectDao.updateSampleModelId(defaultModelId);
        log.error("[txtanls]:文本分析项目初始化示例项目训练id条数：{}", updateCount);

    }

    /**
     * 对比传递的参数和数据库中存储的数据，判断是否重新训练
     *
     * @param srcProject 前端传递的参数
     * @param project    文本分析项目
     * @return Boolean 是否重新训练
     * @author jy.zhao
     * @date 2021/11/24 15:25
     **/
    public Boolean isRetrainProject(ProjectDO srcProject, ProjectDO project) {
        // 时间字段id不相等，重新训练
        if (!StrUtil.equals(project.getTimeFieldId(), srcProject.getTimeFieldId())) {
            return true;
        }
        // 数据源表id不相等，重新训练
        if (!StrUtil.equals(project.getTableId(), srcProject.getTableId())) {
            return true;
        }
        List<ProjectFieldDO> srcTextFieldList = srcProject.getTextFieldList();
        List<ProjectFieldDO> textFieldList = project.getTextFieldList();
        // srcTextFieldList、textFieldList为空或长度为0，重新训练。 srcTextFieldList、textFieldList正常情况下不为空
        if (CollUtil.isEmpty(srcTextFieldList) || CollUtil.isEmpty(textFieldList)) {
            return true;
        }
        if (srcTextFieldList.size() != textFieldList.size()) {
            return true;
        }
        // 查看分析的字段是否有修改
        // 由于前面判断两个数组长度相等，就直接返回了，所以使用containsAll就相当于判断两个数组是否相等
        return !srcTextFieldList.containsAll(textFieldList);
    }
}
